package dissertation.extensions.generate.webservice.webmethod.control;

import com.intellij.codeInsight.CodeInsightActionHandler;
import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.codeInsight.generation.PsiMethodMember;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import dissertation.common.utils.WriteAction;
import dissertation.extensions.generate.webservice.webmethod.view.WebMethodChooserDialogWrapper;
import dissertation.services.ejbclass.EJBClassService;
import dissertation.services.ejbmethod.EJBMethodService;
import dissertation.services.ejbclass.IEJBClassService;
import dissertation.services.ejbmethod.IEJBMethodService;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static dissertation.common.constants.Constants.*;

/**
 * Class for handling the generate web method event
 */
public class GenerateWebMethodCodeInsightActionHandler implements CodeInsightActionHandler {
    private IEJBClassService ejbClassService;
    private IEJBMethodService ejbMethodService;

    public GenerateWebMethodCodeInsightActionHandler() {
        ejbClassService = new EJBClassService();
        ejbMethodService = new EJBMethodService();
    }

    @Override
    public void invoke(@NotNull final Project project, @NotNull Editor editor, @NotNull PsiFile file) {
        final PsiClass aClass = OverrideImplementUtil.getContextClass(project, editor, file, true);
        if (aClass == null) {
            HintManager.getInstance().showErrorHint(editor, "An Error occurred while retrieving the containing Java file.");
        } else {
            PsiMethod [] psiMethods = aClass.getMethods();
            List<CandidateInfo> candidates = createCandidates(aClass, psiMethods);

            final PsiMethodMember[] psiMethodMembers = convertToMethodMembers(candidates);

            if (psiMethodMembers == null || psiMethodMembers.length == 0) {
                HintManager.getInstance().showErrorHint(editor, "No method left to convert into webservice");
            } else {
                WebMethodChooserDialogWrapper webMethodChooserDialogWrapper = new WebMethodChooserDialogWrapper(psiMethodMembers, project);
                webMethodChooserDialogWrapper.show();
                if (webMethodChooserDialogWrapper.isOK()) {
                    final List<PsiMethodMember> selectedElements = webMethodChooserDialogWrapper.getSelectedElements();
                    if (selectedElements != null && !selectedElements.isEmpty()) {
                        Runnable cmd = new Runnable() {
                            @Override
                            public void run() {
                                ejbClassService.addImportStatement(aClass, WEB_SERVICE_PACKAGE);
                                PsiMethod psiMethod = (PsiMethod) selectedElements.get(0).getPsiElement();
                                ejbMethodService.annotateEJBMethod(psiMethod, WEB_METHOD_ANNOTATION_NAME);
                            }
                        };
                        CommandProcessor.getInstance().executeCommand(project, new WriteAction(cmd), null, null);
                    }
                }
            }
        }
    }

    private List<CandidateInfo> createCandidates(PsiClass aClass, PsiMethod[] psiMethods) {
        List<CandidateInfo> candidates = new ArrayList<CandidateInfo>();
        for (PsiMethod psiMethod : psiMethods) {
            if (psiMethod.getModifierList().findAnnotation(WEB_SERVICE_PACKAGE + "." + WEB_METHOD_ANNOTATION_NAME) == null
                    && psiMethod.getModifierList().hasExplicitModifier(PUBLIC_MODIFIER)
                    && !psiMethod.getModifierList().hasExplicitModifier(STATIC_MODIFIER)) {
                candidates.add(new CandidateInfo(psiMethod, null, aClass, true));
            }
        }
        return candidates;
    }

    public boolean startInWriteAction() {
        return false;
    }

    private PsiMethodMember[] convertToMethodMembers(Collection<CandidateInfo> candidates) {
        return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function<CandidateInfo, PsiMethodMember>() {
            public PsiMethodMember fun(final CandidateInfo s) {
                return new PsiMethodMember(s);
            }
        });
    }
}
